UNPKG

@defikitdotnet/education-module-ai

Version:
761 lines 86.3 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var jsx_runtime_1 = require("react/jsx-runtime"); var react_1 = require("react"); var router_1 = require("next/router"); var useCourses_1 = require("../../../hooks/useCourses"); var immer_1 = require("immer"); var button_1 = require("../../../components/ui/button"); var input_1 = require("../../../components/ui/input"); var textarea_1 = require("../../../components/ui/textarea"); var select_1 = require("../../../components/ui/select"); var card_1 = require("../../../components/ui/card"); var accordion_1 = require("../../../components/ui/accordion"); var sonner_1 = require("sonner"); var lucide_react_1 = require("lucide-react"); var CategoryManager_1 = __importDefault(require("../../../components/CategoryManager")); var tabs_1 = require("../../../components/ui/tabs"); var file_upload_1 = require("../../../components/ui/file-upload"); var react_markdown_1 = __importDefault(require("react-markdown")); var label_1 = require("../../../components/ui/label"); // Add mappings for display names (similar to create.tsx) var levelNames = { beginner: "Beginner", intermediate: "Intermediate", advanced: "Advanced", }; var visibilityNames = { private: "Private", public: "Public", }; // Define lecture types for the select dropdown var lectureTypes = [ { value: "text", label: "Text" }, { value: "video", label: "Video URL" }, { value: "h5p", label: "H5P Content ID/URL" }, ]; var CourseEditPage = function () { var _a, _b, _c, _d, _e, _f, _g, _h, _j; var router = (0, router_1.useRouter)(); var courseId = typeof router.query.id === "string" ? router.query.id : null; var _k = (0, react_1.useState)(""), teacherId = _k[0], setTeacherId = _k[1]; var _l = (0, react_1.useState)(null), course = _l[0], setCourse = _l[1]; var _m = (0, react_1.useState)(true), isLoading = _m[0], setIsLoading = _m[1]; var _o = (0, react_1.useState)(""), error = _o[0], setError = _o[1]; var _p = (0, react_1.useState)(false), isEditing = _p[0], setIsEditing = _p[1]; // --- State for editable data --- var _q = (0, react_1.useState)(null), editableCourseData = _q[0], setEditableCourseData = _q[1]; // --- State for thumbnail tab --- var _r = (0, react_1.useState)("url"), thumbnailTab = _r[0], setThumbnailTab = _r[1]; // Default to URL if existing // --- State for fetched category names --- var _s = (0, react_1.useState)(null), categoryDetails = _s[0], setCategoryDetails = _s[1]; var _t = (0, react_1.useState)(false), isLoadingCategories = _t[0], setIsLoadingCategories = _t[1]; // --- State for Quiz Preview Modal --- var _u = (0, react_1.useState)(false), isQuizPreviewOpen = _u[0], setIsQuizPreviewOpen = _u[1]; var _v = (0, react_1.useState)(null), selectedQuizPreview = _v[0], setSelectedQuizPreview = _v[1]; // --- State for Lecture Preview Modal --- var _w = (0, react_1.useState)(false), isLecturePreviewOpen = _w[0], setIsLecturePreviewOpen = _w[1]; var _x = (0, react_1.useState)(null), selectedLecturePreview = _x[0], setSelectedLecturePreview = _x[1]; var _y = (0, useCourses_1.useCourses)(teacherId, process.env.NEXT_PUBLIC_BACKEND_URL || "/api"), fetchCourseDetail = _y.fetchCourseDetail, updateCourse = _y.updateCourse, deleteCourse = _y.deleteCourse; // Get teacher ID from various sources (0, react_1.useEffect)(function () { // First check URL query parameter var queryTeacherId = typeof router.query.teacherId === "string" ? router.query.teacherId : null; if (queryTeacherId) { setTeacherId(queryTeacherId); } else if (typeof window !== "undefined") { // Check localStorage for teacher info var storedTeacher = localStorage.getItem('currentTeacher'); if (storedTeacher) { try { var teacherData = JSON.parse(storedTeacher); if (teacherData && teacherData.id) { setTeacherId(teacherData.id); } } catch (e) { console.error("Failed to parse teacher data:", e); } } } }, [router.query.teacherId]); // --- Fetch Category Details Function --- var fetchCategoryDetails = (0, react_1.useCallback)(function (categoryIds) { return __awaiter(void 0, void 0, void 0, function () { var categoryPromises, fetchedCategories, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!categoryIds || categoryIds.length === 0) { setCategoryDetails([]); // No categories to fetch return [2 /*return*/]; } setIsLoadingCategories(true); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); categoryPromises = categoryIds.map(function (id) { return fetch("".concat(process.env.NEXT_PUBLIC_BACKEND_URL || "/api", "/api/categories/").concat(id)).then(function (res) { if (!res.ok) throw new Error("Failed to fetch category ".concat(id)); return res.json(); }); }); return [4 /*yield*/, Promise.all(categoryPromises)]; case 2: fetchedCategories = _a.sent(); setCategoryDetails(fetchedCategories); return [3 /*break*/, 5]; case 3: err_1 = _a.sent(); console.error("Error fetching category details:", err_1); sonner_1.toast.error("Could not load category names."); // Set to empty array or null depending on desired error display setCategoryDetails(null); return [3 /*break*/, 5]; case 4: setIsLoadingCategories(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, []); // Removed apiBaseUrl from dependency array as it uses process.env directly // Fetch course details - Modified to not depend on teacherId var loadCourseData = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () { var courseData, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!courseId) { setError("Course ID is missing."); setIsLoading(false); return [2 /*return*/]; } _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); setIsLoading(true); setError(""); return [4 /*yield*/, fetchCourseDetail(courseId)]; case 2: courseData = _a.sent(); if (!courseData) { throw new Error("Course not found or failed to fetch."); } console.log("Loaded course data:", courseData); setCourse(courseData); // Reset category details before fetching new ones setCategoryDetails(null); // Fetch category names after getting course data if ((courseData === null || courseData === void 0 ? void 0 : courseData.categories) && Array.isArray(courseData.categories)) { fetchCategoryDetails(courseData.categories); } else { setCategoryDetails([]); // No categories } return [3 /*break*/, 5]; case 3: err_2 = _a.sent(); console.error("Error loading course:", err_2); setError(err_2 instanceof Error ? err_2.message : "Failed to load course details"); setCourse(null); return [3 /*break*/, 5]; case 4: setIsLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [courseId, fetchCourseDetail, fetchCategoryDetails]); (0, react_1.useEffect)(function () { if (courseId) { loadCourseData(); } }, [courseId, loadCourseData]); // --- Handler to start editing --- var handleStartEditing = function () { var _a; if (!course) return; // Helper function to map fetched quiz question to UpdateQuizQuestionDTO var mapQuizQuestion = function (q) { var _a; return ({ id: q.id, question: q.question, options: Array.isArray(q.options) ? q.options : [], correctAnswer: (_a = q.answer) !== null && _a !== void 0 ? _a : "", // Map 'answer' to 'correctAnswer' explanation: q.explanation || "", }); }; // Helper function to map fetched quiz to UpdateQuizDTO var mapQuiz = function (quizData) { var _a; return ({ id: quizData.id, title: quizData.title, description: quizData.description || "", questions: (_a = (quizData.questions && Array.isArray(quizData.questions) ? quizData.questions.map(mapQuizQuestion) : [])) !== null && _a !== void 0 ? _a : [], }); }; setEditableCourseData({ title: course.title, description: course.description || "", thumbnail: course.thumbnail || "", level: course.level, visibility: course.visibility, categories: Array.isArray(course.categories) ? course.categories : [], // Ensure sections are sorted and mapped correctly sections: ((_a = course.sections) !== null && _a !== void 0 ? _a : []) .sort(function (a, b) { return a.orderIndex - b.orderIndex; }) .map(function (section) { var _a; return ({ id: section.id, title: section.title, description: section.description || "", orderIndex: section.orderIndex, // Lectures mapping remains the same lectures: ((_a = section.lectures) !== null && _a !== void 0 ? _a : []) .sort(function (a, b) { return a.orderIndex - b.orderIndex; }) .map(function (lecture) { var mappedType = "text"; if (["text", "video", "h5p"].includes(lecture.type)) { mappedType = lecture.type; } else if (lecture.type === "presentation") { mappedType = "h5p"; } return { id: lecture.id, title: lecture.title, content: lecture.content, type: mappedType, orderIndex: lecture.orderIndex, }; }), // Initialize quizzes array - put existing single quiz into array if it exists // This assumes the fetched data still has `section.quiz` quizzes: section.quiz ? [mapQuiz(section.quiz)] : [], }); }), }); setIsEditing(true); setThumbnailTab(course.thumbnail && !course.thumbnail.startsWith("http") ? "upload" : "url"); }; // --- Handler to cancel editing --- var handleCancelEditing = function () { setEditableCourseData(null); setIsEditing(false); setError(""); }; // --- Refactored Handle course update --- var handleSubmit = function (e) { return __awaiter(void 0, void 0, void 0, function () { var payloadToSubmit, updatedCourseData, err_3, errorMessage; return __generator(this, function (_a) { switch (_a.label) { case 0: e.preventDefault(); if (!courseId || !editableCourseData) return [2 /*return*/]; payloadToSubmit = __assign(__assign({}, editableCourseData), { sections: editableCourseData.sections.map(function (editableSection, sectionIndex) { var _a; // Create the section object expected by the backend DTO var backendSection = { id: editableSection.id, title: editableSection.title, description: editableSection.description, orderIndex: sectionIndex, // Ensure order index is updated lectures: editableSection.lectures.map(function (lecture, lectureIndex) { return (__assign(__assign({}, lecture), { orderIndex: lectureIndex })); }), // Take the first quiz from the array, or null if empty quiz: editableSection.quizzes && editableSection.quizzes.length > 0 ? __assign(__assign({}, editableSection.quizzes[0]), { // Ensure questions within the first quiz are properly formatted if needed questions: ((_a = editableSection.quizzes[0].questions) !== null && _a !== void 0 ? _a : []).map(function (q) { return (__assign({}, q)); }) // Simple map for now }) : null, }; return backendSection; }) }); console.log("Submitting update payload (mapped back to single quiz):", payloadToSubmit); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); setIsLoading(true); setError(""); return [4 /*yield*/, updateCourse(courseId, payloadToSubmit)]; case 2: updatedCourseData = _a.sent(); if (!updatedCourseData) { throw new Error("Update failed: Course data not returned."); } // Reset state with newly fetched data structure setCourse(updatedCourseData); if (updatedCourseData.categories && Array.isArray(updatedCourseData.categories)) { fetchCategoryDetails(updatedCourseData.categories); } else { setCategoryDetails([]); } setEditableCourseData(null); // Clear editing state setIsEditing(false); sonner_1.toast.success("Course updated successfully!"); return [3 /*break*/, 5]; case 3: err_3 = _a.sent(); console.error("Error updating course:", err_3); errorMessage = err_3 instanceof Error ? err_3.message : "Failed to update course"; setError(errorMessage); sonner_1.toast.error("Error updating course: ".concat(errorMessage)); return [3 /*break*/, 5]; case 4: setIsLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }; var handleDelete = function () { return __awaiter(void 0, void 0, void 0, function () { var err_4; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!courseId || !confirm("Are you sure you want to delete this course? This cannot be undone.")) return [2 /*return*/]; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); setIsLoading(true); return [4 /*yield*/, deleteCourse(courseId)]; case 2: _a.sent(); sonner_1.toast.success("Course deleted successfully!"); router.push("/teacher?teacherId=".concat(teacherId)); return [3 /*break*/, 4]; case 3: err_4 = _a.sent(); setError(err_4 instanceof Error ? err_4.message : "Failed to delete course"); sonner_1.toast.error("Error deleting course: ".concat(err_4 instanceof Error ? err_4.message : "Unknown error")); setIsLoading(false); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; // --- Handler for basic course field changes --- var handleFieldChange = function (field, value) { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft && field in draft) { // Type assertion needed because 'field' is a key, and TS struggles with indexed access assignment draft[field] = value; } })); }; // --- Handlers for Section changes --- var handleSectionChange = function (sectionIndex, field, // Use EditableSection type value) { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) { draft.sections[sectionIndex][field] = value; } })); }; var handleAddSection = function () { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft) { var newOrderIndex = draft.sections.length; draft.sections.push({ // No 'id' for new sections title: "New Section ".concat(newOrderIndex + 1), description: "", orderIndex: newOrderIndex, // Set initial order index lectures: [], quizzes: [], }); } })); }; var handleDeleteSection = function (sectionIndex) { if (!confirm("Are you sure you want to delete this section and all its content?")) return; setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft === null || draft === void 0 ? void 0 : draft.sections) { draft.sections.splice(sectionIndex, 1); // Re-calculate orderIndex after deletion is not strictly necessary here // as the submit handler will fix it, but could be done for consistency: // draft.sections.forEach((sec, idx) => sec.orderIndex = idx); } })); }; var handleMoveSection = function (sectionIndex, direction) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; if (!(draft === null || draft === void 0 ? void 0 : draft.sections)) return; var sections = draft.sections; var targetIndex = direction === "up" ? sectionIndex - 1 : sectionIndex + 1; if (targetIndex >= 0 && targetIndex < sections.length) { // Swap elements _a = [ sections[targetIndex], sections[sectionIndex], ], sections[sectionIndex] = _a[0], sections[targetIndex] = _a[1]; // Update orderIndex immediately (optional but good practice) // sections[sectionIndex].orderIndex = sectionIndex; // sections[targetIndex].orderIndex = targetIndex; // The submit handler will set final correct order anyway } })); }; // --- Handlers for Lecture changes --- var handleLectureChange = function (sectionIndex, lectureIndex, field, // Exclude fields not directly edited here value // Handle type change specifically ) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; if ((_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.lectures[lectureIndex]) { draft.sections[sectionIndex].lectures[lectureIndex][field] = value; } })); }; var handleAddLecture = function (sectionIndex) { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) { var section = draft.sections[sectionIndex]; var newOrderIndex = section.lectures.length; section.lectures.push({ // No 'id' for new lectures title: "New Lecture ".concat(newOrderIndex + 1), content: "", type: "text", // Default type orderIndex: newOrderIndex, // Set initial order index }); } })); }; var handleDeleteLecture = function (sectionIndex, lectureIndex) { if (!confirm("Are you sure you want to delete this lecture?")) return; setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; if ((_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.lectures) { draft.sections[sectionIndex].lectures.splice(lectureIndex, 1); // Re-calculate orderIndex after deletion (optional here) // draft.sections[sectionIndex].lectures.forEach((lec, idx) => lec.orderIndex = idx); } })); }; var handleMoveLecture = function (sectionIndex, lectureIndex, direction) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; var _b; var lectures = (_b = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _b === void 0 ? void 0 : _b.lectures; if (!lectures) return; var targetIndex = direction === "up" ? lectureIndex - 1 : lectureIndex + 1; if (targetIndex >= 0 && targetIndex < lectures.length) { _a = [ lectures[targetIndex], lectures[lectureIndex], ], lectures[lectureIndex] = _a[0], lectures[targetIndex] = _a[1]; // Update orderIndex (optional here) // lectures[lectureIndex].orderIndex = lectureIndex; // lectures[targetIndex].orderIndex = targetIndex; } })); }; // --- Updated Handlers for Quiz changes (using quizzes array) --- var handleQuizChange = function (sectionIndex, quizIndex, // Add quizIndex field, value) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; // Target the correct quiz in the array var quiz = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]; if (quiz) { quiz[field] = value; } })); }; // Updated to push to quizzes array var handleAddQuiz = function (sectionIndex) { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) { // Ensure quizzes array exists if (!Array.isArray(draft.sections[sectionIndex].quizzes)) { draft.sections[sectionIndex].quizzes = []; } var newQuizIndex = draft.sections[sectionIndex].quizzes.length; // Push a new quiz object into the array draft.sections[sectionIndex].quizzes.push({ // No 'id' for new quiz yet title: "New Quiz ".concat(newQuizIndex + 1), description: "", questions: [], }); } })); }; // Updated to splice from quizzes array var handleDeleteQuiz = function (sectionIndex, quizIndex) { if (!confirm("Are you sure you want to delete this quiz?")) return; setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; if ((_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes) { draft.sections[sectionIndex].quizzes.splice(quizIndex, 1); // Splice by index } })); }; // --- Updated Handlers for Quiz Question changes --- var handleQuizQuestionChange = function (sectionIndex, quizIndex, // Add quizIndex questionIndex, field, value) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a, _b; var question = (_b = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]) === null || _b === void 0 ? void 0 : _b.questions[questionIndex]; // Target correct quiz/question if (question) { question[field] = value; if (field === "correctAnswer" && !question.options.includes(value)) { console.warn("Correct answer doesn't match any current option."); } } })); }; // Updated to add question to the correct quiz var handleAddQuizQuestion = function (sectionIndex, quizIndex) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a; var quiz = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]; // Target correct quiz if (quiz) { // Ensure questions array exists if (!Array.isArray(quiz.questions)) { quiz.questions = []; } var questionIndex = quiz.questions.length; quiz.questions.push({ // No id for new question question: "New Question ".concat(questionIndex + 1), options: ["Option 1", "Option 2"], correctAnswer: "Option 1", explanation: "", }); } })); }; // Updated to delete question from the correct quiz var handleDeleteQuizQuestion = function (sectionIndex, quizIndex, // Add quizIndex questionIndex) { if (!confirm("Are you sure you want to delete this question?")) return; setEditableCourseData((0, immer_1.produce)(function (draft) { var _a, _b; var questions = (_b = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]) === null || _b === void 0 ? void 0 : _b.questions; // Target correct quiz if (questions) { questions.splice(questionIndex, 1); } })); }; // --- Updated Handlers for Quiz Question Option changes --- var handleQuizOptionChange = function (sectionIndex, quizIndex, // Add quizIndex questionIndex, optionIndex, value) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a, _b; var question = (_b = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]) === null || _b === void 0 ? void 0 : _b.questions[questionIndex]; // Target correct quiz/question if (question === null || question === void 0 ? void 0 : question.options) { var oldOptionValue = question.options[optionIndex]; question.options[optionIndex] = value; if (question.correctAnswer === oldOptionValue) { question.correctAnswer = value; } } })); }; // Updated to add option to the correct quiz/question var handleAddQuizOption = function (sectionIndex, quizIndex, questionIndex) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a, _b; var question = (_b = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]) === null || _b === void 0 ? void 0 : _b.questions[questionIndex]; // Target correct quiz/question if (question) { // Ensure options array exists if (!Array.isArray(question.options)) { question.options = []; } question.options.push("New Option ".concat(question.options.length + 1)); } })); }; // Updated to delete option from the correct quiz/question var handleDeleteQuizOption = function (sectionIndex, quizIndex, // Add quizIndex questionIndex, optionIndex) { setEditableCourseData((0, immer_1.produce)(function (draft) { var _a, _b; var question = (_b = (_a = draft === null || draft === void 0 ? void 0 : draft.sections[sectionIndex]) === null || _a === void 0 ? void 0 : _a.quizzes[quizIndex]) === null || _b === void 0 ? void 0 : _b.questions[questionIndex]; // Target correct quiz/question if ((question === null || question === void 0 ? void 0 : question.options) && question.options.length > 1) { var deletedOption = question.options.splice(optionIndex, 1)[0]; if (question.correctAnswer === deletedOption && question.options.length > 0) { question.correctAnswer = question.options[0]; } else if (question.options.length === 0) { question.correctAnswer = ""; } } else { sonner_1.toast.info("Cannot delete the last option."); } })); }; // --- Handler for category changes in edit mode --- var handleEditableCategoriesChange = function (selectedCategoryObjects) { setEditableCourseData((0, immer_1.produce)(function (draft) { if (draft) { draft.categories = selectedCategoryObjects.map(function (cat) { return cat.id; }); } })); }; // --- Handler for file upload --- var handleFileUpload = function (fileUrl) { // Assuming fileUrl is relative like "/uploads/image.jpg" // Prepend the API base URL if necessary, or store relative path based on setup // const fullUrl = `${process.env.NEXT_PUBLIC_BACKEND_URL}${fileUrl}`; // Example if full URL needed handleFieldChange("thumbnail", fileUrl); // Store relative path setThumbnailTab("upload"); // Switch tab to show preview if needed }; // --- Loading / Error / Not Found States --- if (isLoading && !course && !editableCourseData) { return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen flex justify-center items-center p-8", children: (0, jsx_runtime_1.jsx)("div", { className: "animate-spin rounded-full h-32 w-32 border-b-2 border-blue-600" }) })); } if (error && !course && !isEditing) { // Added !isEditing condition to allow showing errors during edit mode below return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen p-8", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "border-red-400", children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-red-700", children: "Error Loading Course" }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-red-700", children: error }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", className: "mt-4", onClick: function () { return router.push("/teacher?teacherId=".concat(teacherId)); }, children: "Back to Dashboard" }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", className: "mt-4 ml-2", onClick: loadCourseData, children: "Retry" })] })] }) })); } if (!course && !isEditing) { // Handled by the loading/error states above if fetch fails // This state might be reached if courseId is invalid but teacherId is present return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen p-8", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "border-yellow-400", children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-yellow-700", children: "Not Found" }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-yellow-700", children: "Course not found or ID is invalid." }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", className: "mt-4", onClick: function () { return router.push("/teacher?teacherId=".concat(teacherId)); }, children: "Back to Dashboard" })] })] }) })); } // --- Main Render --- // Use editable data if editing, otherwise use the fetched course data var displayData = isEditing ? editableCourseData : course; // Guard against rendering before data is ready (especially displayData) if (!displayData && isEditing) { // This might happen briefly when starting to edit return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen flex justify-center items-center p-8", children: (0, jsx_runtime_1.jsx)("p", { children: "Preparing edit mode..." }) })); } // Ensure displayData is not null before accessing its properties if (!displayData) { return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen flex justify-center items-center p-8", children: [(0, jsx_runtime_1.jsx)("p", { children: "Loading course data..." }), " "] })); } // Helper to get full thumbnail URL var getThumbnailUrl = function (thumbnailPath) { if (!thumbnailPath) return "/images/placeholder.jpg"; // If it's already a full URL if (thumbnailPath.startsWith("http://") || thumbnailPath.startsWith("https://")) { return thumbnailPath; } // If it's a relative path from the API uploads if (thumbnailPath.startsWith("/uploads")) { return "".concat(process.env.NEXT_PUBLIC_BACKEND_URL || "").concat(thumbnailPath); } // Otherwise, assume it's a placeholder or needs specific handling // For safety, return placeholder if unsure return "/images/placeholder.jpg"; }; return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen bg-gray-50 pb-16", children: [(0, jsx_runtime_1.jsx)("header", { className: "bg-white shadow-sm sticky top-0 z-10", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsxs)("h1", { className: "text-2xl font-semibold text-gray-900 truncate pr-4", children: [isEditing ? "Edit Course:" : "", " ", (_a = displayData === null || displayData === void 0 ? void 0 : displayData.title) !== null && _a !== void 0 ? _a : "Loading..."] }), (0, jsx_runtime_1.jsx)("div", { className: "flex space-x-2 flex-shrink-0", children: isEditing ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", onClick: handleCancelEditing, disabled: isLoading, children: "Cancel" }), (0, jsx_runtime_1.jsx)(button_1.Button, { onClick: handleSubmit, disabled: isLoading, children: isLoading ? "Saving..." : "Save Changes" })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", onClick: function () { return router.push("/teacher?teacherId=".concat(teacherId)); }, disabled: !teacherId, children: "Dashboard" }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "default", onClick: handleStartEditing, disabled: !course || isLoading, children: "Edit Course" })] })) })] }), isEditing && error && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded text-sm", children: (0, jsx_runtime_1.jsxs)("p", { children: [(0, jsx_runtime_1.jsx)("strong", { children: "Error:" }), " ", error] }) }))] }) }), (0, jsx_runtime_1.jsx)("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: (0, jsx_runtime_1.jsxs)("div", { className: "md:grid md:grid-cols-3 md:gap-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "md:col-span-1", children: (0, jsx_runtime_1.jsxs)("div", { className: "px-4 sm:px-0 sticky top-[calc(theme(spacing.16)+1rem)] space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-medium leading-6 text-gray-900", children: isEditing ? "Edit Information" : "Course Actions" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-gray-600", children: isEditing ? "Update course details, level, visibility, and categories." : "View course details or manage content." })] }), isEditing && editableCourseData ? ((0, jsx_runtime_1.jsx)(card_1.Card, { children: (0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "pt-6 space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "title", children: "Title" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "title", value: editableCourseData.title, onChange: function (e) { return handleFieldChange("title", e.target.value); }, placeholder: "Course Title", required: true, className: "mt-1" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "description", children: "Description" }), (0, jsx_runtime_1.jsx)(textarea_1.Textarea, { id: "description", value: editableCourseData.description, onChange: function (e) { return handleFieldChange("description", e.target.value); }, placeholder: "Course Description", rows: 4, className: "mt-1" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { className: "block mb-1", children: "Course Thumbnail" }), (0, jsx_runtime_1.jsxs)(tabs_1.Tabs, { value: thumbnailTab, onValueChange: function (value) { return setThumbnailTab(value); }, className: "w-full", children: [(0, jsx_runtime_1.jsxs)(tabs_1.TabsList, { className: "grid w-full grid-cols-2 mb-2", children: [(0, jsx_runtime_1.jsxs)(tabs_1.TabsTrigger, { value: "upload", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Upload, { className: "h-4 w-4 mr-2" }), " Upload"] }), (0, jsx_runtime_1.jsxs)(tabs_1.TabsTrigger, { value: "url", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LinkIcon, { className: "h-4 w-4 mr-2" }), " URL"] })] }), (0, jsx_runtime_1.jsxs)(tabs_1.TabsContent, { value: "upload", children: [(0, jsx_runtime_1.jsx)(file_upload_1.FileUpload, { onFileUpload: handleFileUpload }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-500 mt-1", children: "Max file size 4MB. Recommended aspect ratio 16:9." })] }), (0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "url", children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: "thumbnail-url", value: editableCourseData.thumbnail || "", onChange: function (e) { handleFieldChange("thumbnail", e.target.value); // If user types a URL, make sure the tab reflects it if (e.target.value.startsWith('http')) setThumbnailTab("url"); }, placeholder: "Enter image URL (e.g., https://...)" }) })] }), editableCourseData.thumbnail && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 border rounded-md p-2 relative h-32 bg-gray-100 flex justify-center items-center", children: (0, jsx_runtime_1.jsx)("img", { src: getThumbnailUrl(editableCourseData.thumbnail), alt: "Thumbnail preview", className: "max-h-full max-w-full object-contain", onError: function (e) { e.target.src = "/images/placeholder.jpg"; e.target.onerror = null; } }) }))] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "level", children: "Level" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { value: editableCourseData.level, onValueChange: function (value) { return handleFieldChange("level", value); }, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { id: "level", className: "mt-1", children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: "Select level" }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: Object.entries(levelNames).map(function (_a) { var key = _a[0], name = _a[1]; return ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: key, children: name }, key)); }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "visibility", children: "Visibility" }), (0, jsx_runtime_1.jsxs)(select_1.Select, { value: editableCourseData.visibility, onValueChange: function (value) { return handleFieldChange("visibility", value); }, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { id: "visibility", className: "mt-1", children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: "Select visibility" }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: Object.entries(visibilityNames).map(function (_a) { var key = _a[0], name = _a[1]; return ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: key, children: name }, key)); }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(label_1.Label, { className: "block mb-1", children: "Categories" }), (0, jsx_runtime_1.jsx)(CategoryManager_1.default, { mode: "select", teacherId: teacherId, apiBaseUrl: process.env.NEXT_PUBLIC_BACKEND_URL || "/api", // Pass only the IDs to the manager selectedCategories: editableCourseData.categories, // Receive the full Category objects back if needed, or just IDs onCategoriesChange: function (selectedIds) { return handleFieldChange("categories", selectedIds); } })] })] }) })) : ( /* Read Mode Actions */ (0, jsx_runtime_1.jsx)("div", { className: "mt-6 space-y-4", children: (0, jsx_runtime_1.jsxs)(button_1.Button, { variant: "destructive", className: "w-full justify-start", onClick: handleDelete, disabled: !course || isLoading, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: "mr-2 h-4 w-4" }), " Delete Course"] }) }))] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-5 md:mt-0 md:col-span-2 space-y-6", children: [!isEditing && course && ((0, jsx_runtime_1.jsxs)(card_1.Card, { children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { children: "Course Details" }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "space-y-4", children: [course.thumbnail && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-sm font-medium text-gray-500 mb-1", children: "Thumbnail" }), (0, jsx_runtime_1.jsx)("div", { className: "border round